package com.notice.qrcode;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

/**
 * Utility methods for retrieving content over HTTP using the more-supported
 * {@code java.net} classes in Android.
 */
public final class HttpHelper {

	private static final Collection<String> REDIRECTOR_DOMAINS = new HashSet<String>(
			Arrays.asList("amzn.to", "bit.ly", "bitly.com", "fb.me", "goo.gl",
					"is.gd", "j.mp", "lnkd.in", "ow.ly", "R.BEETAGG.COM",
					"r.beetagg.com", "SCN.BY", "su.pr", "t.co", "tinyurl.com",
					"tr.im"));

	private HttpHelper() {
	}

	public enum ContentType {
		/** HTML-like content type, including HTML, XHTML, etc. */
		HTML,
		/** JSON content */
		JSON,
		/** Plain text content */
		TEXT,
	}

	/**
	 * @param uri
	 *            URI to retrieve
	 * @param type
	 *            expected text-like MIME type of that content
	 * @return content as a {@code String}
	 * @throws IOException
	 *             if the content can't be retrieved because of a bad URI,
	 *             network problem, etc.
	 */
	public static String downloadViaHttp(String uri, ContentType type)
			throws IOException {
		String contentTypes;
		switch (type) {
		case HTML:
			contentTypes = "application/xhtml+xml,text/html,text/*,*/*";
			break;
		case JSON:
			contentTypes = "application/json,text/*,*/*";
			break;
		case TEXT:
		default:
			contentTypes = "text/*,*/*";
		}
		return downloadViaHttp(uri, contentTypes);
	}

	private static String downloadViaHttp(String uri, String contentTypes)
			throws IOException {
		URL url = new URL(uri);
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setRequestProperty("Accept", contentTypes);
		connection.setRequestProperty("Accept-Charset", "utf-8,*");
		connection.setRequestProperty("User-Agent", "ZXing (Android)");
		try {
			connection.connect();
			if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
				throw new IOException("Bad HTTP response: "
						+ connection.getResponseCode());
			}
			return consume(connection);
		} finally {
			connection.disconnect();
		}
	}

	private static String getEncoding(URLConnection connection) {
		String contentTypeHeader = connection.getHeaderField("Content-Type");
		if (contentTypeHeader != null) {
			int charsetStart = contentTypeHeader.indexOf("charset=");
			if (charsetStart >= 0) {
				return contentTypeHeader.substring(charsetStart
						+ "charset=".length());
			}
		}
		return "UTF-8";
	}

	private static String consume(URLConnection connection) throws IOException {
		String encoding = getEncoding(connection);
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		InputStream in = connection.getInputStream();
		try {
			in = connection.getInputStream();
			byte[] buffer = new byte[1024];
			int bytesRead;
			while ((bytesRead = in.read(buffer)) > 0) {
				out.write(buffer, 0, bytesRead);
			}
		} finally {
			try {
				in.close();
			} catch (IOException ioe) {
				// continue
			}
		}
		try {
			return new String(out.toByteArray(), encoding);
		} catch (UnsupportedEncodingException uee) {
			try {
				return new String(out.toByteArray(), "UTF-8");
			} catch (UnsupportedEncodingException uee2) {
				// can't happen
				throw new IllegalStateException(uee2);
			}
		}
	}

	public static URI unredirect(URI uri) throws IOException {
		if (!REDIRECTOR_DOMAINS.contains(uri.getHost())) {
			return uri;
		}
		URL url = uri.toURL();

		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setInstanceFollowRedirects(false);
		connection.setDoInput(false);
		connection.setRequestMethod("HEAD");
		connection.setRequestProperty("User-Agent", "ZXing (Android)");
		try {
			connection.connect();
			switch (connection.getResponseCode()) {
			case HttpURLConnection.HTTP_MULT_CHOICE:
			case HttpURLConnection.HTTP_MOVED_PERM:
			case HttpURLConnection.HTTP_MOVED_TEMP:
			case HttpURLConnection.HTTP_SEE_OTHER:
			case 307: // No constant for 307 Temporary Redirect ?
				String location = connection.getHeaderField("Location");
				if (location != null) {
					try {
						return new URI(location);
					} catch (URISyntaxException e) {
						// nevermind
					}
				}
			}
			return uri;
		} finally {
			connection.disconnect();
		}
	}

}
